﻿using System;
using System.Linq.Expressions;
using System.Text;
using fluentvaly.Results;

namespace fluentvaly
{
    public class MemberValidator<T, TMember> : IValidationStatementSetUp<T, TMember>, IValidationStatement
    {
        public MemberValidator(Expression<Func<T, TMember>> member, string defaultMessage, InvalidationReasons defaultReason)
        {
            MemberToValidate = member;
            InvalidationMessage = defaultMessage;
            InvalidationReason = defaultReason;
        }

        protected Expression<Func<T, TMember>> MemberToValidate { get; private set; }
        protected string InvalidationMessage { get; private set; }
        protected InvalidationReasons InvalidationReason { get; private set; }
        protected bool ReverseValidation { get; set; }
        protected object ValidationExpression { get; set; }
        protected object IfExpression { get; set; }

        public IValidationStatementSetUp<T, TMember> If(Expression<Func<TMember, bool>> predicate)
        {
            IfExpression = predicate;
            return this;
        }

        public IValidationStatementSetUp<T, TMember> If(Expression<Func<T, TMember, bool>> predicate)
        {
            IfExpression = predicate;
            return this;
        }

        public IValidationStatementSetUp<T, TMember> Is(Expression<Func<T, TMember, bool>> predicate)
        {
            ReverseValidation = false;
            ValidationExpression = predicate;
            return this;
        }

        public IValidationStatementSetUp<T, TMember> Is(Expression<Func<TMember, bool>> predicate)
        {
            ReverseValidation = false;
            ValidationExpression = predicate;
            return this;
        }

        public void IsValid(Expression<Func<T, TMember>> memberToValidate)
        {
            
        }

        public IValidationStatementSetUp<T, TMember> IsNot(Expression<Func<TMember, bool>> predicate)
        {
            ReverseValidation = true;
            ValidationExpression = predicate;
            return this;
        }

        public IValidationStatementSetUp<T, TMember> IsNot(Expression<Func<T, TMember, bool>> predicate)
        {
            ReverseValidation = true;
            ValidationExpression = predicate;
            return this;
        }

        public IValidationStatementSetUp<T, TMember> FailWith(InvalidationReasons reason, string message)
        {
            InvalidationReason = reason;
            InvalidationMessage = message;
            return this;
        }

        public IValidationStatementSetUp<T, TMember> FailWith(InvalidationReasons reason)
        {
            InvalidationReason = reason;
            return this;
        }

        ValidationResult IValidationStatement.Validate(object objectToValidate)
        {
            if (ExecuteFunction(IfExpression, objectToValidate))
            {
                if (ReverseValidation ^ ExecuteFunction(ValidationExpression, objectToValidate))
                    return ValidationResult.Pass();
                return new ValidationResult
                           {
                               Member = MemberToValidate.ToString(),
                               Passed = false,
                               Reasons = InvalidationReason,
                               ValidatedObject = objectToValidate.GetType(),
                               Validator = (this).ToString()
                           };
            }
            return ValidationResult.Pass();
        }

        bool IValidationStatement.CantRun(object objectToValidate)
        {
            return ExecuteFunction(IfExpression, objectToValidate);
        }

        public override string ToString()
        {
            var messageBuilder = new StringBuilder();
            messageBuilder.AppendFormat("For object {0} ", typeof(T).FullName);
            messageBuilder.AppendFormat("ensure that {0} ", GetMemberName(MemberToValidate));

            if (ReverseValidation) messageBuilder.Append("is not");
            else messageBuilder.Append("is");

            messageBuilder.AppendFormat(" {0}", GetFunctionText(ValidationExpression));

            if (IfExpression != null)
                messageBuilder.AppendFormat(" if {0}", GetFunctionText(IfExpression));

            return messageBuilder.ToString();
        }

        #region Private helpers
        private bool ExecuteFunction(object function, object objectToValidate)
        {
            if (function == null)
                return true;
            if (function is Expression<Func<TMember, bool>>)
                return ((Expression<Func<TMember, bool>>)function).Compile().Invoke(MemberToValidate.Compile().Invoke((T)objectToValidate));
            if (function is Expression<Func<T, TMember, bool>>)
                return ((Expression<Func<T, TMember, bool>>)function).Compile().Invoke((T)objectToValidate, MemberToValidate.Compile().Invoke((T)objectToValidate));
            return true;
        }

        private string GetMemberName(Expression<Func<T, TMember>> member)
        {
            if (member == null)
                return string.Empty;

            var retVal = member.Body.ToString();
            return retVal.Substring(retVal.IndexOf('.') + 1);
        }

        private string GetFunctionText(object function)
        {
            if (function == null)
                return string.Empty;

            if(function is Expression<Func<TMember, bool>>)
            {
                var tmp = ((Expression<Func<TMember,bool>>) function);
                return tmp.Body.ToString();
            }

            if(function is Expression<Func<T, TMember, bool>>)
            {
                var tmp = ((Expression<Func<T, TMember, bool>>) function);
                return tmp.Body.ToString();
            }
            return string.Empty;
        }
        #endregion
    }
}
